package com.system.cacheClient.client.impl;

import com.system.cacheClient.client.CacheClient;
import com.system.cacheClient.config.CacheClientProperties;
import com.system.cacheClient.enums.CacheInvalidationMode;
import com.system.cacheClient.enums.CacheType;
import com.system.cacheClient.feign.*;
import com.system.cacheClient.common.CacheR;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import jakarta.annotation.PostConstruct;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
 * 缓存客户端实现类
 * 实现多级缓存逻辑：根据配置决定是否先从本地缓存获取，获取不到再从服务端获取
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class CacheClientImpl implements CacheClient {
    
    /**
     * 缓存客户端配置属性
     */
    private final CacheClientProperties cacheClientProperties;
    
    /**
     * 各种类型的Feign客户端
     */
    private final StringCacheFeignClient stringCacheFeignClient;
    private final HashCacheFeignClient hashCacheFeignClient;
    private final ListCacheFeignClient listCacheFeignClient;
    private final SetCacheFeignClient setCacheFeignClient;
    private final ZSetCacheFeignClient zSetCacheFeignClient;
    
    /**
     * 本地缓存，使用Caffeine实现
     */
    private Cache<String, Object> localCache;
    
    /**
     * 缓存失效模式
     */
    private CacheInvalidationMode invalidationMode;
    
    /**
     * 初始化本地缓存和失效模式
     */
    @PostConstruct
    public void init() {
        // 初始化本地缓存
        if (cacheClientProperties.isLocalCache()) {
            localCache = Caffeine.newBuilder()
                    .maximumSize(cacheClientProperties.getLocalCacheMaxSize())
                    .expireAfterWrite(cacheClientProperties.getLocalCacheExpireAfterWrite(), TimeUnit.MINUTES)
                    .build();
            log.info("本地缓存已启用，最大大小: {}，过期时间: {}分钟", 
                    cacheClientProperties.getLocalCacheMaxSize(), 
                    cacheClientProperties.getLocalCacheExpireAfterWrite());
        } else {
            localCache = null;
            log.info("本地缓存已禁用，所有请求将直接访问服务端");
        }
        
        // 初始化失效模式
        invalidationMode = CacheInvalidationMode.fromCode(cacheClientProperties.getInvalidationMode());
        log.info("缓存失效模式: {}", invalidationMode == CacheInvalidationMode.ACTIVE ? "主动失效" : "被动失效");
    }
    
    @Override
    public <T> CacheR<T> get(CacheType cacheType, String key) {
        // 如果开启了本地缓存，先从本地缓存获取
        if (localCache != null) {
            T localValue = (T) localCache.getIfPresent(key);
            if (localValue != null) {
                log.debug("从本地缓存获取到数据，key: {}", key);
                return CacheR.success(localValue);
            }
        }
        
        // 本地缓存没有或者未开启本地缓存，从服务端获取
        try {
            CacheR<T> result = getFromServer(cacheType, key);
            if (result.getCode() == 200 && result.getData() != null && localCache != null) {
                // 将从服务端获取的数据放入本地缓存（如果开启了本地缓存）
                localCache.put(key, result.getData());
                log.debug("从服务端获取到数据并存入本地缓存，key: {}", key);
            }
            return result;
        } catch (Exception e) {
            log.error("从服务端获取缓存数据失败，key: {}", key, e);
            return CacheR.fail("获取缓存数据失败: " + e.getMessage());
        }
    }
    
    /**
     * 从服务端获取数据
     * @param cacheType 缓存类型
     * @param key 键
     * @return 数据
     */
    private <T> CacheR<T> getFromServer(CacheType cacheType, String key) {
        switch (cacheType) {
            case STRING:
                return stringCacheFeignClient.get(key);
            case HASH:
                return (CacheR<T>) hashCacheFeignClient.get(key);
            case LIST:
                return (CacheR<T>) listCacheFeignClient.get(key);
            case SET:
                return (CacheR<T>) setCacheFeignClient.get(key);
            case ZSET:
                return (CacheR<T>) zSetCacheFeignClient.get(key);
            default:
                return stringCacheFeignClient.get(key);
        }
    }
    
    @Override
    public <T> CacheR<Boolean> set(CacheType cacheType, String key, T data) {
        // 先更新服务端
        try {
            CacheR<Boolean> result = setToServer(cacheType, key, data);
            if (result.getCode() == 200 && Boolean.TRUE.equals(result.getData()) && localCache != null) {
                // 更新成功后更新本地缓存（如果开启了本地缓存）
                localCache.put(key, data);
                log.debug("设置缓存成功，key: {}", key);
            }
            return result;
        } catch (Exception e) {
            log.error("设置缓存数据失败，key: {}", key, e);
            return CacheR.fail("设置缓存数据失败: " + e.getMessage());
        }
    }
    
    /**
     * 设置服务端数据
     * @param cacheType 缓存类型
     * @param key 键
     * @param data 数据
     * @return 操作结果
     */
    private <T> CacheR<Boolean> setToServer(CacheType cacheType, String key, T data) {
        switch (cacheType) {
            case STRING:
                return stringCacheFeignClient.set(key, data);
            case HASH:
                return (CacheR<Boolean>) hashCacheFeignClient.set(key, (java.util.Map<String, String>) data);
            case LIST:
                return (CacheR<Boolean>) listCacheFeignClient.set(key, (java.util.List<String>) data);
            case SET:
                return (CacheR<Boolean>) setCacheFeignClient.set(key, (java.util.Set<String>) data);
            case ZSET:
                return (CacheR<Boolean>) zSetCacheFeignClient.set(key, (java.util.Set<String>) data);
            default:
                return stringCacheFeignClient.set(key, data);
        }
    }
    
    @Override
    public CacheR<Boolean> delete(CacheType cacheType, String key) {
        // 根据失效模式执行不同的删除逻辑
        if (invalidationMode == CacheInvalidationMode.ACTIVE) {
            // 主动失效模式 - 直接删除缓存
            return performActiveInvalidation(cacheType, key);
        } else {
            // 被动失效模式 - 设置过期时间为0
            return performPassiveInvalidation(cacheType, key);
        }
    }
    
    /**
     * 执行主动失效
     * @param cacheType 缓存类型
     * @param key 缓存键
     * @return 操作结果
     */
    private CacheR<Boolean> performActiveInvalidation(CacheType cacheType, String key) {
        // 先删除服务端数据
        try {
            CacheR<Boolean> result = deleteFromServer(cacheType, key);
            if (result.getCode() == 200 && Boolean.TRUE.equals(result.getData()) && localCache != null) {
                // 删除本地缓存（如果开启了本地缓存）
                localCache.invalidate(key);
                log.debug("主动删除缓存成功，key: {}", key);
            }
            return result;
        } catch (Exception e) {
            log.error("主动删除缓存数据失败，key: {}", key, e);
            return CacheR.fail("删除缓存数据失败: " + e.getMessage());
        }
    }
    
    /**
     * 删除服务端数据
     * @param cacheType 缓存类型
     * @param key 键
     * @return 操作结果
     */
    private CacheR<Boolean> deleteFromServer(CacheType cacheType, String key) {
        switch (cacheType) {
            case STRING:
                return stringCacheFeignClient.delete(key);
            case HASH:
                return hashCacheFeignClient.delete(key);
            case LIST:
                return listCacheFeignClient.delete(key);
            case SET:
                return setCacheFeignClient.delete(key);
            case ZSET:
                return zSetCacheFeignClient.delete(key);
            default:
                return stringCacheFeignClient.delete(key);
        }
    }
    
    /**
     * 执行被动失效
     * @param cacheType 缓存类型
     * @param key 缓存键
     * @return 操作结果
     */
    private CacheR<Boolean> performPassiveInvalidation(CacheType cacheType, String key) {
        // 设置服务端缓存过期时间为0
        try {
            CacheR<Boolean> result = expireOnServer(cacheType, key, 0);
            if (result.getCode() == 200 && Boolean.TRUE.equals(result.getData()) && localCache != null) {
                // 设置本地缓存过期时间为0
                localCache.invalidate(key);
                log.debug("被动失效缓存成功，key: {}", key);
            }
            return result;
        } catch (Exception e) {
            log.error("被动失效缓存数据失败，key: {}", key, e);
            return CacheR.fail("失效缓存数据失败: " + e.getMessage());
        }
    }
    
    /**
     * 设置服务端缓存过期时间
     * @param cacheType 缓存类型
     * @param key 键
     * @param timeout 过期时间
     * @return 操作结果
     */
    private CacheR<Boolean> expireOnServer(CacheType cacheType, String key, long timeout) {
        switch (cacheType) {
            case STRING:
                return stringCacheFeignClient.expire(key, timeout);
            case HASH:
                return hashCacheFeignClient.expire(key, timeout);
            case LIST:
                return listCacheFeignClient.expire(key, timeout);
            case SET:
                return setCacheFeignClient.expire(key, timeout);
            case ZSET:
                return zSetCacheFeignClient.expire(key, timeout);
            default:
                return stringCacheFeignClient.expire(key, timeout);
        }
    }
    
    @Override
    public <T> CacheR<Boolean> set(String key, T data) {
        // 根据数据类型自动判断缓存类型
        CacheType cacheType = determineCacheType(data);
        return set(cacheType, key, data);
    }
    
    @Override
    public <T> CacheR<T> get(CacheType cacheType, String key, Supplier<T> supplier) {
        CacheR<T> result = get(cacheType, key);
        if (result.getCode() == 200 && result.getData() != null) {
            return result;
        }
        
        // 缓存中没有数据，执行回调函数获取数据
        try {
            T data = supplier.get();
            if (data != null) {
                // 将数据存入缓存
                set(cacheType, key, data);
            }
            return CacheR.success(data);
        } catch (Exception e) {
            log.error("执行回调函数获取数据失败，key: {}", key, e);
            return CacheR.fail("获取数据失败: " + e.getMessage());
        }
    }
    
    /**
     * 根据数据类型判断缓存类型
     * @param data 数据
     * @return 缓存类型
     */
    private CacheType determineCacheType(Object data) {
        if (data == null) {
            return CacheType.STRING;
        }
        
        Class<?> clazz = data.getClass();
        if (clazz == String.class || clazz.isPrimitive() || isWrapperType(clazz)) {
            return CacheType.STRING;
        } else if (data instanceof java.util.List) {
            return CacheType.LIST;
        } else if (data instanceof java.util.Set) {
            return CacheType.SET;
        } else if (data instanceof java.util.Map) {
            return CacheType.HASH;
        } else {
            // 默认使用字符串类型
            return CacheType.STRING;
        }
    }
    
    /**
     * 判断是否为包装类型
     * @param clazz 类型
     * @return 是否为包装类型
     */
    private boolean isWrapperType(Class<?> clazz) {
        return clazz == Boolean.class || clazz == Character.class ||
                clazz == Byte.class || clazz == Short.class ||
                clazz == Integer.class || clazz == Long.class ||
                clazz == Float.class || clazz == Double.class;
    }
}